#!/usr/bin/env python

import socket
import struct

class LongestComposition():
    def __init__( self, words, jobs ):
        self.wordset = set(words)
        self.words = list(self.wordset)
        self.result = ""
        self.jobsize = (len(words)+jobs-1)/jobs

    def composed(self, word):
        for i in range(1,len(word)):
            pre = word[0:i]
            post = word[i:len(word)+1]
            if ( pre in self.wordset and
                 ( post in self.wordset or self.composed(post))):
                return True
        return False

    def do_work(self, job ):
        begin = self.jobsize*job
        for word in self.words[begin:begin+self.jobsize]:
            if ( len(word) > len(self.result) and 
                 self.composed(word )):
                self.result = word

class Socket:
    def __init__(self, port):
        self.port = port
        sock = socket.socket(socket.AF_INET, 
                             socket.SOCK_DGRAM, 
                             socket.IPPROTO_UDP)
        sock.setsockopt(socket.SOL_SOCKET, 
                        socket.SO_REUSEADDR, 1)
        sock.bind(('', port))
        mreq = struct.pack("=4sl", 
                           socket.inet_aton("224.51.105.104"), 
                           socket.INADDR_ANY)
        sock.setsockopt(socket.IPPROTO_IP, 
                    socket.IP_ADD_MEMBERSHIP, 
                    mreq)
        sock.setblocking(False)
        self.sock = sock

    def dataReady(self):
        import select
        (r,w,x) = select.select([self.sock],[],[],0)
        if len(r):
            return True
        return False

    def waitDataReady(self):
        import select
        (r,w,x) = select.select([self.sock],[],[])

    def recv(self):
        s = self.sock.recv(10240)
        (a,b,c) = s.split("\n")
        return (a,b,c)

    def dispatchEvents(self,obj):
        while self.dataReady():
            (cmd,arg1,arg2) = self.recv()
            if ( "claim" == cmd ):
                obj.onClaim(arg1,int(arg2))
            elif ( "done" == cmd ):
                obj.onDone(arg1,int(arg2))
            elif ( "answer" == cmd ):
                obj.onAnswer(arg1)

    def sendClaim(self,res,id):
        self.send("claim",res,str(id))

    def sendDone(self,res,id):
        self.send("done",res,str(id))

    def sendAnswer(self,res):
        self.send("answer",res,"")

    def send(self, a, b, c):
        return self.sock.sendto("\n".join([a,b,c]), 
                                ("224.51.105.104", self.port) )

# Inappropriately named 'StateMachine"
class SM():
    def __init__(self, port, words,jobs, sock ):
        import random
        self.lc = LongestComposition( words,jobs )
        self.sock = sock
        self.jobs = range(0,jobs)
        random.shuffle(self.jobs)
    def do_work(self, n):
        self.lc.do_work(n)
    def run(self):
        i = 0
        while len(self.jobs):
            job = self.jobs[0]
            self.remove(job)
            i += 1
            self.sock.sendClaim(self.lc.result,job)
            self.do_work(job)
            self.sock.sendDone(self.lc.result,job)
            self.sock.dispatchEvents(self)
        self.sock.sendAnswer(self.lc.result)
        print "jobs performed %d"%i
        print self.lc.result
        
    def setResult(self,res):
        if ( len(res) > len(self.lc.result) ):
            self.lc.result = res
                 
    def onClaim(self, res, id ):
        self.moveEnd(id)
        self.setResult(res)

    def onDone(self, res, id ):
        self.remove(id)
        self.setResult(res)

    def onAnswer(self, res ):
        self.jobs = []
        self.setResult(res)

    def moveEnd(self, val):
        try:
            self.jobs.remove(val)
            self.jobs.append(val)
        except:
            pass
        
    def remove(self, val):
        try:
            self.jobs.remove(int(val))
        except:
            pass

def daemon(port,jobs):
    sock = Socket(port)
    while 1:
        sock.waitDataReady()
        (cmd,filename,jobs) = sock.recv()
        if "start" == cmd:
            print "Reading %s"%(filename)
            f = open(filename,'r')
            words = [ i.strip() for i in f ]
            sm = SM(port,words,int(jobs),sock)
            sm.run()

class Monitor():
    def __init__(self, port):
        self.sock = Socket(port)
    def run(self,jobs,filename):
        self.sock.send("start",filename,str(jobs))
        while 1:
            self.sock.waitDataReady()
            (op,result,b) = self.sock.recv()
            if ("answer" == op):
                print len(result),result
                break

if __name__ == "__main__":
    from optparse import OptionParser
    parser = OptionParser()
    parser.add_option("-p", "--port", dest="port",
                      type="int",
                      help="port", default=1234 )
    parser.add_option("-j", "--jobs", dest="jobs",
                      type="int",
                      help="split into jobs", default=1024 )
    parser.add_option("-s", "--start", dest="start",
                      action="store_true",
                      help="Starter", default=False )
    parser.add_option("-f", "--file", dest="file",
                      type="string",
                      help="filename", default="words.txt" )

    (options, args) = parser.parse_args()

    if ( options.start ):
        mon = Monitor(options.port)
        mon.run(options.jobs,
                options.file)
    else:
        daemon(options.port, options.jobs)

    
    
